package com.darkflame.client.gui;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;

import com.darkflame.client.GreenFruitEnginev2;
import com.darkflame.client.SuperSimpleSemantics;
import com.darkflame.client.query.Query;
import com.darkflame.client.semantic.QueryEngine;
import com.darkflame.client.semantic.QueryEngine.DoSomethingWithNodesRunnable;
import com.darkflame.client.semantic.RawQueryUtilities;
import com.darkflame.client.semantic.SSSNode;
import com.darkflame.client.semantic.SSSNodesWithCommonProperty;
import com.google.gwt.user.client.History;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.DisclosurePanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.Hyperlink;
import com.google.gwt.user.client.ui.RequiresResize;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.KeyPressHandler;
import com.google.gwt.event.dom.client.KeyPressEvent;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;

/** Everything the user needs to ask and display a supersimplesemantic query**/
public class QueryInterface extends Composite implements RequiresResize  {

	//help/explanation box
	ExplanationBox explanationbox = new ExplanationBox();
	
	/** a visualiser for the resulting node "flowchart" after a query is made **/
	RelationshipVisualiser nv = new RelationshipVisualiser();
	
	CheckBox visualiserEnabled = new CheckBox("Visualiser Enabled",true);

	final AbsolutePanel overallContainer = new AbsolutePanel();
	
	final VerticalPanel verticalPanel = new VerticalPanel();
	
	final VerticalPanel titleAndSearchBox = new VerticalPanel();
	
	/** The box the user types in to specify the query
	 *  **/
	final SearchBox userSearchBox = new SearchBox();


	final VerticalPanel rawResultBox = new VerticalPanel();
	final DisclosurePanel rawResults = new DisclosurePanel("(Matching Results)");

	final QueryDisplayer queryAsUnderstood = new QueryDisplayer();

	HorizontalPanel runQueryHorPanel = new HorizontalPanel();

	final Button runQuery = new Button("Run Query");

	/** index list management 
	 * That is , this controls the trusted index's the GFE will search and make deductions from **/
	TrustedIndexList indexlist = new TrustedIndexList();

	//interface text
	final HTML title = new HTML("<div style=\"font-size: 2.5em; display:block; width:auto; text-align: center;margin: 10px;\">Green Fruit Engine Beta</div>");
	final Label enterQueryLabel = new Label("Enter query:");


	QueryInterface thisQI = this;

	//after results we might want to do stuff.
	enum PostProcess{
		none,pickrandom,count
	}
	
	PostProcess CurrentPostProcess = PostProcess.none;
			
	
	//create the query interface page
	public QueryInterface() {	
		initWidget(overallContainer);
		
		
		//setup styles
		rawResultBox.getElement().getStyle().setBackgroundColor("White");
		rawResultBox.getElement().getStyle().setOpacity(0.9);
		rawResultBox.getElement().getStyle().setBorderColor("#EEEEEE");
		
	
		overallContainer.setSize("100%", "100%");
		
		//dragpanel is background
		overallContainer.add(nv,0,0);
		
		//add widgets		
		overallContainer.add(titleAndSearchBox,0,0);

		//put the index list in the corner
		overallContainer.add(indexlist,10,10);
		indexlist.getElement().getStyle().setZIndex(1500);
		overallContainer.add(explanationbox,Window.getClientWidth()-350,10);
		explanationbox.getElement().getStyle().setZIndex(1500);
		
		
		
		
		//retrieves a list of sites from a text file
		//think of this as an "index of indexs"
		//This stores the uri's of all *.ntlist indexs the user might wish to use
		indexlist.addSitesFrom("DatabaseList.txt");
		

		titleAndSearchBox.addStyleName("SearchBoxContainer");
		titleAndSearchBox.setSpacing(3);
		titleAndSearchBox.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_CENTER);
		titleAndSearchBox.getElement().getStyle().setZIndex(500);
		
		titleAndSearchBox.setWidth("100%");		
		titleAndSearchBox.add(title);
		titleAndSearchBox.add(enterQueryLabel);

		userSearchBox.setWidth("500px");	
		userSearchBox.setHeight("30px");	
		
		userSearchBox.setTotalSteps(100);
		userSearchBox.setCurrentStep(1);
		
		titleAndSearchBox.add(userSearchBox);
		
		//Tell SuperSimpleSemantics to update this searchbox during progress
		//Note; At the moment, the searchbox's "internal progress bar" doesnt work correctly
		SuperSimpleSemantics.setGenericSearchMonitor(userSearchBox);

		titleAndSearchBox.add(queryAsUnderstood);
		titleAndSearchBox.add(runQueryHorPanel);

		rawResults.addStyleName("SearchResultContainer");		
		rawResults.add(rawResultBox);		
		
		
		//rawResults.addAttachHandler(new Handler() {
		//	
		//	@Override
		//	public void onAttachOrDetach(AttachEvent event) {
		//		thisQI.onResize();
		//	}
		//});
		
		//we make this float under the button  rather then in the vertical panel
		//should be updated onresize too
		
		overallContainer.add(rawResults,(overallContainer.getOffsetWidth()/2)-(rawResults.getOffsetWidth()/2),174);
		//GreenFruitEnginev3.log("rawResults.getOffsetWidth()="+rawResults.getOffsetWidth());
		
		
		rawResults.setVisible(false);

		//query interface disabled till indexs are loaded
		this.setSearchEnabled(false);
		
		
		
		//verticalPanel.add(rawResults);

		Label lblNewLabel = new Label("results");
		rawResultBox.add(lblNewLabel);
		rawResults.getElement().getStyle().setZIndex(1600);
		
		History.addValueChangeHandler( new ValueChangeHandler<String>() {

			@Override
			public void onValueChange(ValueChangeEvent<String> event) {
				
				userSearchBox.setText(event.getValue());
				GreenFruitEnginev2.gotoQueryPage();
				
				Scheduler.get().scheduleDeferred(new ScheduledCommand() {    
					  @Override
					  public void execute() {
						  processUserQueryAndDisplay();

							userSearchBox.setCurrentStep(1);
							runQuery.setEnabled(true);
							rawResults.setVisible(true);
					  }});
				
				userSearchBox.setCurrentStep(100);
				
			}
		});




		runQuery.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {

				runQuery.setEnabled(false);
				userSearchBox.setCurrentStep(20);
								
				//update the history
				History.newItem(userSearchBox.getText(), false);

				//process and display only after the above changes 
				//have had a time to display
				SuperSimpleSemantics.waitFor.scheduleAfter(new Runnable() {
					
					@Override
					public void run() {

					  	processUserQueryAndDisplay();
					  
						userSearchBox.setCurrentStep(1);
						runQuery.setEnabled(true);
						rawResults.setVisible(true);
					}
				});
				
				/*
				Scheduler.get().scheduleDeferred(new ScheduledCommand() {    
					  @Override
					  public void execute() {
						  
						  	processUserQueryAndDisplay();
						  
							userSearchBox.setCurrentStep(1);
							runQuery.setEnabled(true);
							rawResults.setVisible(true);
					  }
				});
				*/

				
				
			}

		});
		runQueryHorPanel.add(runQuery);
		visualiserEnabled.setValue(true);
		
		runQueryHorPanel.add(visualiserEnabled);
		userSearchBox.addKeyPressHandler(new KeyPressHandler() {
			@Override
			public void onKeyPress(KeyPressEvent event) {
				if(event.getCharCode()==13){
					runQuery.click();
				}
			}
		});
		
		
		this.onResize();
	}
	
	

	/**The magic starts here - 
	 * - takes the users text input and turns it into query object storing the request
	 * - displays that object for checking 
	 * - finds nodes that meets that query and displays them 
	 * **/ 
	private void processUserQueryAndDisplay() {

		//clear existing data
		rawResultBox.clear();
		queryAsUnderstood.clear();

		//open the result box so we can see the results when they come
		rawResults.setOpen(true);
		
		//test if its a debug request
		String textQuery = userSearchBox.getText();
		
		textQuery = testForDebugRequest(textQuery);
		
		if (textQuery==""){
			return;
		}

		//try to get a query from whatever the human entered
		Query proccessedQuery = new Query(textQuery);

		//display the interpreted query
		queryAsUnderstood.populateWithQuery(proccessedQuery);

		//prepare the output callback (should this be declared earlier? will it ever change?)
		class outputCode implements DoSomethingWithNodesRunnable{

			@Override
			public void run(ArrayList<SSSNode> resultNodes,boolean invert) {
				
				OverallInterface.debugDisplayer.log("Query Finnished:"+resultNodes.toString(),"Green");
								
				//make sure there's results, else we output a special message
				if (resultNodes.size()==0){
					
					rawResultBox.clear();
					rawResultBox.add(new Label("(no results found)"));
					
					return;
				}
				
				//test for postProcess
				resultNodes = postProcess(resultNodes);
								
				//output the result 
				outputAllResults(resultNodes);
			}
			
		}		
	
		//prepare the per-node output callback (should this be declared earlier? will it ever change?)
		class addNodeToOutputDisplay implements DoSomethingWithNodesRunnable{

					@Override
					public void run(ArrayList<SSSNode> resultNodes,boolean invert) {
						Label rlab = new Label("test 1");			
						rawResultBox.add(rlab);
					}
					
				}		
		
		//get result only if raw query contained no errors
		//ArrayList<SSSNode> result = new ArrayList<SSSNode>();
		
		if (proccessedQuery.hasNoErrors()){
			//get the result
			//result = 
					QueryEngine.processQuery(proccessedQuery,false,new addNodeToOutputDisplay(),new outputCode());

		} else {
			//moan about the error
			Label rlab = new Label("no results found due to error");			
			rawResultBox.add(rlab);
			return;
		}



	}



	public ArrayList<SSSNode> postProcess(ArrayList<SSSNode> resultNodes) {
		
		if (CurrentPostProcess==PostProcess.pickrandom){
			
			int i = resultNodes.size();
			SuperSimpleSemantics.log(" ----------------------------------picking random out of  "+i);
			int pr = (int) Math.round((Math.random()*i));				
			SuperSimpleSemantics.log(" ----------------------------------picked  "+pr);	
			
			SSSNode picked = resultNodes.get(pr);
			
			resultNodes.clear();
			resultNodes.add(picked);					
		}
		
		return resultNodes;
	}



	public void outputAllResults(ArrayList<SSSNode> result) {
		
		
		
		Iterator<SSSNode> reit = result.iterator();

		while (reit.hasNext()) {
			
			SSSNode sssNode = reit.next();
			Hyperlink rlab = new Hyperlink(sssNode.getPLabel(),"\""+sssNode.getPLabel()+"\""); 
			
		//	Label rlab = new Label(sssNode.getPLabel());
			rlab.setTitle(sssNode.getPURI());

			rawResultBox.add(rlab);


		}
		
		
		//display on new visualiser 
		nv.clearAllWidgets();
		if (visualiserEnabled.getValue()){
			Collections.reverse(result);
			nv.populate(result);
		}
	}

	public String testForDebugRequest(String textQuery) {
		CurrentPostProcess=PostProcess.none;
		if (textQuery.startsWith("<PickRandom:")){

			CurrentPostProcess=PostProcess.pickrandom;
			textQuery = textQuery.substring(12);
			textQuery = textQuery.substring(0, textQuery.length()-1);
			
			return textQuery;
		}
		//
		if (textQuery.equalsIgnoreCase("<allsets>")){

			rawResultBox.add(getAllPropertySets());
			return "";
		}
		
		//
		if (textQuery.startsWith("<ViewDatabase:")){

			//ensure its loaded
			
			//turn to the set page 
			GreenFruitEnginev2.gotoLoadedPropertyListPage();
			
			return "";
		}
		
		if (textQuery.startsWith("<node:")){

			int endsAt=textQuery.indexOf(">");
			String nodeToGet = textQuery.substring(6, endsAt);

			SSSNode requested = SSSNode.getNodeByLabel(nodeToGet);


			displaySingleResult(requested);
			return "";
		}
		if (textQuery.startsWith("<nodeUri:")){

			int endsAt=textQuery.indexOf(">");
			String nodeToGet = textQuery.substring(9, endsAt);

			SSSNode requested = SSSNode.getNodeByUri(nodeToGet);

			displaySingleResult(requested);
			return "";
		}

		if (textQuery.equalsIgnoreCase("<allprefixs>")){

			Iterator<String> preit = RawQueryUtilities.getPrefixs().keySet().iterator();
			while (preit.hasNext()) {

				String prefix = preit.next();
				String value = RawQueryUtilities.getPrefixs().get(prefix);

				rawResultBox.add(new Label(value+" = "+prefix));

			}


			return "";
		}
		
		return textQuery;
	}

	public void displaySingleResult(SSSNode requested) {

		if (requested==null || requested==SSSNode.NOTFOUND){
			rawResultBox.add(new Label("No matching nodes found"));
			return;

		}

		rawResultBox.add(new NodeResultLabel(requested));

		ArrayList<SSSNode> result = new ArrayList<SSSNode>();
		result.add(requested);
		nv.populate(result);
	}

	private VerticalPanel getAllPropertySets() {		

		Iterator<SSSNodesWithCommonProperty> allsets = SSSNodesWithCommonProperty.globalNodesWithPropertyListByPredicate.values().iterator();
		VerticalPanel container = new VerticalPanel();

		while (allsets.hasNext()) {

			SSSNodesWithCommonProperty set = allsets
					.next();

			Label nl = new Label("-"+set.getCommonPrec().getPURI()+"|"+set.getCommonValue().getPURI());
			container.add(nl);

		}

		return container;
	}



	public void postLoadInterfaceUpdates() {
		

		//query interface disabled till indexs are loaded so we enable here
		this.setSearchEnabled(true);

		indexlist.cleanUpAfterLoad();
		onResize(); 
	}



	@Override
	public void onResize() {

		overallContainer.add(rawResults,-100,-100);
		
		//update result overlay position
		int x = (overallContainer.getOffsetWidth()/2)-(rawResults.getOffsetWidth()/2);
		int y = runQuery.getAbsoluteTop()+runQuery.getOffsetHeight()+8;
				
		overallContainer.add(rawResults,x,y);
		
	}
	
	@Override
	public void onLoad(){
		this.onResize();
	}
	
	
	public void setSearchEnabled(Boolean status){
		userSearchBox.setEnabled(status);
		runQuery.setEnabled(status);
		visualiserEnabled.setEnabled(status);
		
	}


	
	
}
